/*
 *
 * canvasResize
 *
 * Version: 1.2.0
 * Date (d/m/y): 02/10/12
 * Update (d/m/y): 14/05/13
 * Original author: @gokercebeci
 * Licensed under the MIT license
 * - This plugin working with binaryajax.js and exif.js
 *   (It's under the MPL License http://www.nihilogic.dk/licenses/mpl-license.txt)
 * Demo: http://canvasResize.gokercebeci.com/
 *
 * - I fixed iOS6 Safari's image file rendering issue for large size image (over mega-pixel)
 *   using few functions from https://github.com/stomita/ios-imagefile-megapixel
 *   (detectSubsampling, )
 *   And fixed orientation issue by using https://github.com/jseidelin/exif-js
 *   Thanks, Shinichi Tomita and Jacob Seidelin
 */

/* global BinaryFile, EXIF */
(function ($) {
  var pluginName = 'canvasResize'
  var methods = {
    newsize: function (w, h, W, H, C) {
      var c = C ? 'h' : ''
      if ((W && w > W) || (H && h > H)) {
        var r = w / h
        if ((r >= 1 || H === 0) && W && !C) {
          w = W
          h = (W / r) >> 0
        } else if (C && r <= (W / H)) {
          w = W
          h = (W / r) >> 0
          c = 'w'
        } else {
          w = (H * r) >> 0
          h = H
        }
      }
      return {
        'width': w,
        'height': h,
        'cropped': c
      }
    },
    dataURLtoBlob: function (data) {
      var mimeString = data.split(',')[0].split(':')[1].split(';')[0]
      var byteString = atob(data.split(',')[1])
      var ab = new ArrayBuffer(byteString.length)
      var ia = new Uint8Array(ab)
      for (var i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i)
      }
      var bb = (window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder)
      if (bb) {
                //    console.log('BlobBuilder');
        bb = new (window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder)()
        bb.append(ab)
        return bb.getBlob(mimeString)
      } else {
                //    console.log('Blob');
        bb = new Blob([ab], {
          'type': (mimeString)
        })
        return bb
      }
    },
        /**
         * Detect subsampling in loaded image.
         * In iOS, larger images than 2M pixels may be subsampled in rendering.
         */
    detectSubsampling: function (img) {
      var iw = img.width
      var ih = img.height
      if (iw * ih > 1048576) { // subsampling may happen over megapixel image
        var canvas = document.createElement('canvas')
        canvas.width = canvas.height = 1
        var ctx = canvas.getContext('2d')
        ctx.drawImage(img, -iw + 1, 0)
                // subsampled image becomes half smaller in rendering size.
                // check alpha channel value to confirm image is covering edge pixel or not.
                // if alpha value is 0 image is not covering, hence subsampled.
        return ctx.getImageData(0, 0, 1, 1).data[3] === 0
      } else {
        return false
      }
    },
        /**
         * Update the orientation according to the specified rotation angle
         */
    rotate: function (orientation, angle) {
      var o = {
                // nothing
        1: {90: 6, 180: 3, 270: 8},
                // horizontal flip
        2: {90: 7, 180: 4, 270: 5},
                // 180 rotate left
        3: {90: 8, 180: 1, 270: 6},
                // vertical flip
        4: {90: 5, 180: 2, 270: 7},
                // vertical flip + 90 rotate right
        5: {90: 2, 180: 7, 270: 4},
                // 90 rotate right
        6: {90: 3, 180: 8, 270: 1},
                // horizontal flip + 90 rotate right
        7: {90: 4, 180: 5, 270: 2},
                // 90 rotate left
        8: {90: 1, 180: 6, 270: 3}
      }
      return o[orientation][angle] ? o[orientation][angle] : orientation
    },
        /**
         * Transform canvas coordination according to specified frame size and orientation
         * Orientation value is from EXIF tag
         */
    transformCoordinate: function (canvas, width, height, orientation) {
      switch (orientation) {
        case 5:
        case 6:
        case 7:
        case 8:
          canvas.width = height
          canvas.height = width
          break
        default:
          canvas.width = width
          canvas.height = height
      }
      var ctx = canvas.getContext('2d')
      switch (orientation) {
        case 1:
                    // nothing
          break
        case 2:
                    // horizontal flip
          ctx.translate(width, 0)
          ctx.scale(-1, 1)
          break
        case 3:
                    // 180 rotate left
          ctx.translate(width, height)
          ctx.rotate(Math.PI)
          break
        case 4:
                    // vertical flip
          ctx.translate(0, height)
          ctx.scale(1, -1)
          break
        case 5:
                    // vertical flip + 90 rotate right
          ctx.rotate(0.5 * Math.PI)
          ctx.scale(1, -1)
          break
        case 6:
                    // 90 rotate right
          ctx.rotate(0.5 * Math.PI)
          ctx.translate(0, -height)
          break
        case 7:
                    // horizontal flip + 90 rotate right
          ctx.rotate(0.5 * Math.PI)
          ctx.translate(width, -height)
          ctx.scale(-1, 1)
          break
        case 8:
                    // 90 rotate left
          ctx.rotate(-0.5 * Math.PI)
          ctx.translate(-width, 0)
          break
        default:
          break
      }
    },
        /**
         * Detecting vertical squash in loaded image.
         * Fixes a bug which squash image vertically while drawing into canvas for some images.
         */
    detectVerticalSquash: function (img, iw, ih) {
      var canvas = document.createElement('canvas')
      canvas.width = 1
      canvas.height = ih
      var ctx = canvas.getContext('2d')
      ctx.drawImage(img, 0, 0)
      var data = ctx.getImageData(0, 0, 1, ih).data
            // search image edge pixel position in case it is squashed vertically.
      var sy = 0
      var ey = ih
      var py = ih
      while (py > sy) {
        var alpha = data[(py - 1) * 4 + 3]
        if (alpha === 0) {
          ey = py
        } else {
          sy = py
        }
        py = (ey + sy) >> 1
      }
      var ratio = py / ih
      return ratio === 0 ? 1 : ratio
    },
    callback: function (d) {
      return d
    },
    extend: function () {
      var target = arguments[0] || {}
      var a = 1
      var al = arguments.length
      var deep = false
      if (target.constructor === Boolean) {
        deep = target
        target = arguments[1] || {}
      }
      if (al === 1) {
        target = this
        a = 0
      }
      var prop
      for (; a < al; a++) {
        if ((prop = arguments[a]) !== null) {
          for (var i in prop) {
            if (target === prop[i]) { continue }
            if (deep && typeof prop[i] === 'object' && target[i]) { methods.extend(target[i], prop[i]) } else if (prop[i] !== undefined) { target[i] = prop[i] }
          }
        }
      }
      return target
    }
  }
  var defaults = {
    width: 300,
    height: 0,
    crop: false,
    quality: 0.8,
    rotate: 0,
    'callback': methods.callback
  }
  function Plugin (file, options) {
    this.file = file
        // EXTEND
    this.options = methods.extend({}, defaults, options)
    this._defaults = defaults
    this._name = pluginName
    this.init()
  }
  Plugin.prototype = {
    init: function () {
            // this.options.init(this);
      var $this = this
      var file = this.file
      var reg = /(.*)(?:\.([^.]+$))/
      var fileAccept = file.name.match(reg)[2]
      var dataAccept = ''

      switch (fileAccept) {
        case 'jpg':
          dataAccept = 'data:image/jpeg;'
          break
        case 'gif':
          dataAccept = 'data:image/gif;'
          break
        case 'png':
          dataAccept = 'data:image/png;'
          break
        default:
          break
      }

      var reader = new FileReader()
      reader.onloadend = function (e) {
        var dataURL = e.target.result
        var originalBase64Length = dataURL.substr(dataURL.indexOf('base64') + 7).length

        if (!file.type.length) {
          dataURL = dataURL.replace('data:', dataAccept)
        }

        var byteString = atob(dataURL.split(',')[1])
        var binary = new BinaryFile(byteString, 0, byteString.length)
        var exif = EXIF.readFromBinaryFile(binary)

        var img = new Image()
        img.onload = function (e) {
          var orientation = exif['Orientation'] || 1
          orientation = methods.rotate(orientation, $this.options.rotate)

                    // CW or CCW ? replace width and height
          var size = (orientation >= 5 && orientation <= 8)
                            ? methods.newsize(img.height, img.width, $this.options.width, $this.options.height, $this.options.crop)
                            : methods.newsize(img.width, img.height, $this.options.width, $this.options.height, $this.options.crop)

          var iw = img.width
          var ih = img.height
          var width = size.width
          var height = size.height

          var canvas = document.createElement('canvas')
          var ctx = canvas.getContext('2d')
          ctx.save()
          methods.transformCoordinate(canvas, width, height, orientation)

                    // over image size
          if (methods.detectSubsampling(img)) {
            iw /= 2
            ih /= 2
          }
          var d = 1024 // size of tiling canvas
          var tmpCanvas = document.createElement('canvas')
          tmpCanvas.width = tmpCanvas.height = d
          var tmpCtx = tmpCanvas.getContext('2d')
          var vertSquashRatio = methods.detectVerticalSquash(img, iw, ih)
          var sy = 0
          while (sy < ih) {
            var sh = sy + d > ih ? ih - sy : d
            var sx = 0
            while (sx < iw) {
              var sw = sx + d > iw ? iw - sx : d
              tmpCtx.clearRect(0, 0, d, d)
              tmpCtx.drawImage(img, -sx, -sy)
              var dx = Math.floor(sx * width / iw)
              var dw = Math.ceil(sw * width / iw)
              var dy = Math.floor(sy * height / ih / vertSquashRatio)
              var dh = Math.ceil(sh * height / ih / vertSquashRatio)
              ctx.drawImage(tmpCanvas, 0, 0, sw, sh, dx, dy, dw, dh)
              sx += d
            }
            sy += d
          }
          ctx.restore()
          tmpCanvas = tmpCtx = null

          // if rotated width and height data replacing issue
          var newcanvas = document.createElement('canvas')
          newcanvas.width = size.cropped === 'h' ? height : width
          newcanvas.height = size.cropped === 'w' ? width : height
          var x = size.cropped === 'h' ? (height - width) * 0.5 : 0
          var y = size.cropped === 'w' ? (width - height) * 0.5 : 0
          var newctx = newcanvas.getContext('2d')
          newctx.drawImage(canvas, x, y, width, height)

          var data
          if (file.type === 'image/png') {
            data = newcanvas.toDataURL(file.type)
          } else {
            data = newcanvas.toDataURL('image/jpeg', ($this.options.quality))
          }

          var b64Str = data.substr(data.indexOf('base64') + 7)
          // CALLBACK
          $this.options.callback(data, newcanvas.width, newcanvas.height, {
            originalWidth: img.width,
            originalHeight: img.height,
            originalBase64Length: originalBase64Length,
            compressedBase64Length: b64Str.length,
            orientation: exif['Orientation'] || 1,
            fileType: fileAccept,
            compressedFileType: file.type === 'image/png' ? 'png' : 'jpeg'
          })
        }
        img.src = dataURL
      }

      reader.onerror = $this.options.onerror || function (e) { console.log(e) }
      reader.readAsDataURL(file)
            // reader.readAsBinaryString(file);
    }
  }
  $[pluginName] = function (file, options) {
    if (typeof file === 'string') { return methods[file](options) } else { return new Plugin(file, options) }
  }
})(window)
